#pragma once
#include "baseutil.h"
#include <boost/scoped_array.hpp>
//TODO test it
//for bool/char/short/int/long etc, but not for classes!!
template<typename T>
class Vector {
protected:
	boost::scoped_array<T> buffer;
	size_t count;
	size_t capcity;
public:
	void dump() {
		cerr << "Count: " << count << " Capcity: " << capcity << endl;
		cerr << "[";
		for (size_t i = 0; i < count; ++i) {
			cerr << buffer[i] << " ";
		}
		cerr << "]";
		cerr << endl;
	}
public:
	Vector(const Vector<T>& other)
			: count(0), capcity(0) {
		copyFrom(other);
	}
	explicit Vector(size_t capcity_)
			: count(0), capcity(0) {
		ensureCapcity(capcity_);
	}
	Vector(const T* other, size_t len)
			: count(0), capcity(0) {
		copyFrom(other, len);
	}
	Vector(): count(0), capcity(0){

	}
	virtual ~Vector(void) {

	}
public:
	void operator=(const Vector<T>& other) {
		copyFrom(other);
	}

	void manage(T* other, size_t len){
		count = capcity = len;
		buffer.reset(other);
	}

	void copyFrom(const T* other, size_t len) {
		if (other && len) {
			count = len;
			capcity = len;
			T* buf = new T[capcity];
			ArrayMove(buf, other, count);
			buffer.reset(buf);
		}
	}

	void copyFrom(const Vector<T>& other) {
		count = other.count;
		capcity = count;
		T* buf = new T[capcity];
		ArrayMove(buf, other.getBuffer(), count);
		buffer.reset(buf);
	}
public:
	size_t inline size() const {
		return count;
	}
	size_t inline length() const {
		return count;
	}
	void inline clear() {
		count = 0;
	}
	void ensureCapcity(size_t capcity_) {
		if (capcity_ > capcity) {
			capcity_ = max(8, capcity_);
			T* newArr = new T[capcity_];
			if (buffer) {
				ArrayMove(newArr, buffer.get(), count);
			}
			buffer.reset(newArr);
			capcity = capcity_;
		}
	}
	size_t inline getCapcity() const {
		return capcity;
	}

	T operator[](int index) const {
		return get(index);
	}
	T& operator[](int index) {
		return getRef(index);
	}
	inline T& getRef(size_t index) {
		assert( index>=0 && index <= count);
		if (index == count) {
			append(T());
		}
		return buffer[index];
	}
	T inline get(size_t index) const {
		assert( index>=0 && index < count);
		return buffer[index];
	}
	T inline last() const {
		assert(count >0);
		return buffer[count - 1];
	}
	T inline first() const {
		assert(count > 0);
		return buffer[0];
	}
	//T& operator[](size_t index){
	//	return at(index);
	//}
	void set(size_t index, const T& val) {
		assert( index <= count);
		if (index == count) {
			append(val);
		} else {
			buffer[index] = val;
		}
	}
	inline T* getBuffer() const {
		return buffer.get();
	}

	bool contains(const T& val) {
		for (size_t i = 0; i < count; ++i) {
			if (getRef(i) == val) {
				return true;
			}
		}
		return false;
	}

	void getRange(Vector<T>& result, size_t from, size_t size) {
		assert(from+size <=count);
		result.clear();
		result.ensureCapcity(size);
		for (int i = 0; i < size; ++i) {
			result.set(i, getRef(from + i));
		}
	}
	void replace(size_t from, size_t length, const T* values, size_t valuesCount) {
		if (!length && !valuesCount) {	//modify nothing
			return;
		}
		assert(from + length <= count);

		size_t newCount = count - length + valuesCount;
		if (buffer.get() && newCount <= capcity) {
			if (length != valuesCount) {
				ArrayMove(buffer.get(), from + valuesCount, buffer.get(), from + length, count - from - length);
			}
			ArrayMove(buffer.get(), from, values, 0, valuesCount);
		} else {
			size_t newCapcity = max(newCount * 2, 8);
			T* arr = new T[newCapcity];
			if (buffer.get()) {
				ArrayMove(arr, buffer.get(), from);
			}
			ArrayMove(arr + from, values, valuesCount);
			if (buffer.get()) {
				ArrayMove(arr + from + valuesCount, buffer.get() + from + length, count - from - length);
			}

			buffer.reset(arr);
			capcity = newCapcity;
		}
		count = newCount;

	}
	void replaceFill(size_t from, size_t length, const T& value, size_t repeatCount) {
		if (!length && !repeatCount) {	//modify nothing
			return;
		}
		assert(from + length <= count);

		size_t newCount = count - length + repeatCount;
		if (buffer && newCount <= capcity) {
			if (length != repeatCount) {
				ArrayMove(buffer.get(), from + repeatCount, buffer.get(), from + length, count - from - length);
			}
			RangeSet(buffer.get(), from, repeatCount, value);
		} else {
			size_t newCapcity = max(8, newCount * 2);
			T* arr = new T[newCapcity];
			if (buffer) {
				ArrayMove(arr, buffer.get(), from);
			}
			RangeSet(arr + from, repeatCount, value);
			if (buffer) {
				ArrayMove(arr, from + repeatCount, buffer.get(), from + length, count - from - length);
			}

			buffer.reset(arr);
			capcity = newCapcity;
		}
		count = newCount;
	}

	void inline append(const T& val) {
		replace(count, 0, &val, 1);
	}
	void inline append(T arr[], size_t len) {
		replace(count, 0, arr, len);
	}
	void inline append(const Vector<T>& other){
		replace(count, 0, other.getBuffer(), other.count);
	}
	void inline insert(size_t index, const T& val) {
		replace(index, 0, &val, 1);
	}
	void inline insert(size_t index, T arr[], size_t len) {
		replace(index, 0, arr, len);
	}
	void inline insert(size_t index, const Vector<int>& other){
		replace(index, 0, other.getBuffer(), other.count);
	}

	void inline remove(size_t index) {
		replace(index, 1, 0, 0);
	}
	void inline remove(size_t index, size_t len) {
		replace(index, len, 0, 0);
	}
	void removeValue(const T& val) {
		for (size_t i = 0; i < count; ++i) {
			if (getRef(i) == val) {
				remove(i);
				return;
			}
		}
	}
	void removeAllValues(const T& val) {
		for (size_t i = 0; i < count;) {
			if (get(i) == val) {
				remove(i);
				continue;
			}else{
				++i;
			}
		}
	}
	void inline erase(size_t index) {
		return remove(index);
	}
	//[from, to)
	void fill(size_t from, size_t to, const T& val) {
		assert( from < count && to <= count);
		for (size_t i = from; i < to; ++i) {
			buffer[i] = val;
		}
	}

	bool equals(const Vector<T>& other) {
		return equals(other.getBuffer(), other.length());
	}
	bool equals(const T* other, size_t len) {
		if (len == length()) {
			if (len == 0) {
				return true;
			} else {
				if (!other) {
					return false;
				} else {
					for (size_t i = 0; i < len; ++i) {
						if (get(i) != other[i]) {
							return false;
						}
					}
					return true;
				}
			}
		}
		return false;
	}

};

class IntVector: public Vector<int> {
public:
	explicit IntVector(size_t capcity_ = 8)
			: Vector<int>(capcity_) {
	}
	void rangeAdd(size_t from, size_t to, int delta) {
		assert( from < size() && to <= size());
		for (size_t i = from; i < to; ++i) {
			buffer[i] += delta;
		}
	}
};
